package com.example.watchs.handler;

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.SuspendExecution;
import com.example.watchs.common.BaseVO;
import com.example.watchs.common.LogUtil;
import com.example.watchs.common.MonitorDTO;
import com.example.watchs.common.PageIn;
import com.example.watchs.config.FileMonitorConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.file.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class FileMonitorHandler {
    private final FileMonitorConfig fileMonitorConfig;
    private final FileConversionHandler fileConversionService;
    private final ExecutorService monitorExecutor = Executors.newSingleThreadExecutor();
    private final Map<String, WatchService> watchServices = new HashMap<>();
    private final AtomicBoolean isMonitoring = new AtomicBoolean(false);
    private Thread monitorThread;
    //停止监控的路径
    private final Queue<String> stopMonitoringQueue = new ConcurrentLinkedQueue<>();
    //开始监控的路径
    private final Queue<MonitorDTO> startMonitoringQueue = new ConcurrentLinkedQueue<>();
    private static final String CLOSE = "close_";
    private static final Cache<String, String> FIFO_CACHE = CacheUtil.newFIFOCache(10);

    @Autowired
    public FileMonitorHandler(FileMonitorConfig fileMonitorConfig, FileConversionHandler fileConversionService) {
        this.fileMonitorConfig = fileMonitorConfig;
        this.fileConversionService = fileConversionService;
    }

    /**
     * 查询列表
     */
    public BaseVO<List<MonitorDTO>> list(PageIn page) {
        BaseVO<List<MonitorDTO>> baseVO = new BaseVO<>();
        List<MonitorDTO> dtoList = fileMonitorConfig.getMonitoredFolders();
        baseVO.setCount(dtoList.size());
        baseVO.setContent(dtoList.stream()
                .skip((long) (page.getPage() - 1) * page.getLimit())
                .limit(page.getLimit()).collect(Collectors.toList()));
        return baseVO;
    }

    public void startMonitoring() {
        if (isMonitoring.compareAndSet(false, true)) {
            monitorThread = new Thread(() -> {
                try {
                    for (MonitorDTO folder : fileMonitorConfig.getMonitoredFolders()) {
                        if (folder == null || !folder.getIsRun()) {
                            continue;
                        }
                        submitNewPath(folder);
                    }
                    /**
                     * 监控运行状态
                     */
                    while (isMonitoring.get()) {
                        /**
                         * 新的监控执行
                         */
                        processStartMonitoringRequests();
                        /**
                         * 执行停止的方法
                         */
                        processStopMonitoringRequests();

                        for (Map.Entry<String, WatchService> entry : watchServices.entrySet()) {
                            if (entry.getValue() != null) {
                                WatchKey key;
                                try {
                                    /**
                                     * 阻塞10毫秒 查看是否有数据进行转换
                                     */
                                    key = entry.getValue().poll(10, TimeUnit.MILLISECONDS);
                                } catch (Exception e) {
                                    watchServices.remove(entry.getKey(), entry.getValue());
                                    continue;
                                }

                                try {
                                    /**
                                     * 如果key为null 则过滤
                                     */
                                    if (key == null) {
                                        continue;
                                    }
                                    for (WatchEvent<?> event : key.pollEvents()) {
                                        LogUtil.info("key:" + CLOSE + entry.getKey());
                                        if (FIFO_CACHE.get(CLOSE + entry.getKey()) != null) {
                                            FIFO_CACHE.remove(CLOSE + entry.getKey());
                                            continue;
                                        }
                                        /**
                                         * 处理监控到的改变的文件或者文件夹
                                         */
                                        handleFileChange(event, entry.getKey());
                                    }
                                    key.reset();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    LogUtil.info(e.getLocalizedMessage());
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    LogUtil.info(e.getLocalizedMessage());
                }
            });
            monitorExecutor.submit(monitorThread);
        } else {
            LogUtil.info("程序已经在运行");
        }
    }

//    public void startMonitoring1() {
//        if (isMonitoring.compareAndSet(false, true)) {
//            Fiber<Void> mainFiber = new Fiber<Void>(() -> {
//                try {
//                    // 初始化已有的监控项
//                    for (MonitorDTO folder : fileMonitorConfig.getMonitoredFolders()) {
//                        if (folder == null || !folder.getIsRun()) {
//                            continue;
//                        }
//                        submitNewPath(folder);
//                    }
//
//                    // 为每个WatchService启动独立的Fiber
//                    startWatchServiceFibers();
//
//                    // 主Fiber处理添加/删除监控请求
//                    while (isMonitoring.get()) {
//                        processStartMonitoringRequests();
//                        processStopMonitoringRequests();
//                        Fiber.sleep(10); // Fiber的sleep不会阻塞线程
//                    }
//                } catch (SuspendExecution e) {
//                    // Fiber挂起是正常的
//                    LogUtil.info(e.getLocalizedMessage());
//                } catch (Exception e) {
//                    LogUtil.info(e.getLocalizedMessage());
//                }
//            });
//            mainFiber.start();
//        } else {
//            LogUtil.info("程序已经在运行");
//        }
//    }

//    private void startWatchServiceFibers() throws SuspendExecution {
//        // 为每个WatchService创建独立的Fiber
//        for (Map.Entry<String, WatchService> entry : watchServices.entrySet()) {
//            if (entry.getValue() != null) {
//                Fiber<Void> watchFiber = new Fiber<Void>(() -> {
//                    monitorSingleWatchServiceWithFiber(entry.getKey(), entry.getValue());
//                });
//                watchFiber.start();
//            }
//        }
//    }

//    private void monitorSingleWatchServiceWithFiber(String path, WatchService watchService) throws SuspendExecution {
//        try {
//            while (isMonitoring.get() && watchServices.containsKey(path)) {
//                try {
//                    // Fiber的take()方法会挂起Fiber而不是线程
//                    WatchKey key = watchService.take(); // 不会阻塞底层线程
//                    if (key != null) {
//                        for (WatchEvent<?> event : key.pollEvents()) {
//                            LogUtil.info("key:" + CLOSE + path);
//                            if (FIFO_CACHE.get(CLOSE + path) != null) {
//                                FIFO_CACHE.remove(CLOSE + path);
//                                continue;
//                            }
//                            handleFileChange(event, path);
//                        }
//                        key.reset();
//                    }
//                } catch (Exception e) {
//                    LogUtil.info("监控" + path + "时发生异常: " + e.getLocalizedMessage());
//                    break;
//                }
//            }
//        } catch (Exception e) {
//            LogUtil.info("Fiber监控异常: " + e.getLocalizedMessage());
//        }
//    }

    /**
     * 通过文件路径监控文件
     */
    public boolean startMonitoringByName(String filePath) {
        // 查找指定名称的监控配置
        MonitorDTO folder = fileMonitorConfig.getMonitoredFolders().stream().filter(k -> k.getFilePath().equals(filePath)).findFirst().orElse(null);
        if (!isMonitoring.get() || folder == null) {
            return false;
        }
        // 将需要添加监控的路径加入队列，由独立线程处理
        startMonitoringQueue.offer(folder);
        folder.setIsRun(true);
        return true;
    }

    /**
     * 提交新的 watchService
     */
    private boolean submitNewPath(MonitorDTO folder) {
        try {
            /**
             * 同一个线程中每个文件都有自己的watchService 方便管理
             */
            WatchService watchService = FileSystems.getDefault().newWatchService();
            /**
             * 对文件以及子文件夹进行监控注册
             */
            registerAllDirectories(folder, watchService);
            /**
             * 将watchService 放置到watchServices中进行统一管理
             */
            watchServices.put(folder.getFilePath(), watchService);
            return true;
        } catch (Exception e) {
            LogUtil.info("启动监控失败: " + folder.getFilePath() + ", 错误信息: " + e.getMessage());
            return false;
        }
    }

    /**
     * 为指定目录及其子目录注册文件监控服务
     */
    private void registerAllDirectories(MonitorDTO dto, WatchService watchService) {
        try (Stream<Path> pathStream = Files.walk(Paths.get(dto.getFilePath()), dto.getMaxDepth() != null ? dto.getMaxDepth() : 0)) {
            pathStream.filter(Files::isDirectory)
                    .forEach(path -> {
                        try {
                            path.register(watchService, getStandardWatchEventKinds(dto.getTypes()));
                        } catch (IOException e) {
                            LogUtil.info("注册目录失败: " + path + ", 错误信息: " + e.getMessage());
                        }
                    });
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.info(e.getLocalizedMessage());
        }
    }

    /**
     * type
     * <p>
     * 1 == create
     * 2 == update
     * 3 == delete
     *
     */
    private WatchEvent.Kind<?>[] getStandardWatchEventKinds(List<Integer> types) {
        if (types != null && !types.isEmpty()) {
            WatchEvent.Kind<?>[] kinds = new WatchEvent.Kind[types.size()];
            for (int i = 0; i < types.size(); i++) {
                switch (types.get(i)) {
                    case 1:
                        kinds[i] = StandardWatchEventKinds.ENTRY_CREATE;
                        break;
                    case 2:
                        kinds[i] = StandardWatchEventKinds.ENTRY_MODIFY;
                        break;
                    case 3:
                        kinds[i] = StandardWatchEventKinds.ENTRY_DELETE;
                        break;
                    default:
                        break;
                }
            }
            return kinds;
        }
        return null;
    }

    /**
     * 停止监控
     */
    public void stopMonitoring() {
        if (isMonitoring.compareAndSet(true, false)) {
            try {
                if (monitorThread != null) {
                    monitorThread.interrupt();
                }
                for (Map.Entry<String, WatchService> watchServiceEntry : watchServices.entrySet()) {
                    watchServiceEntry.getValue().close();
                }
                watchServices.clear();
            } catch (Exception e) {
                LogUtil.info(e.getLocalizedMessage());
            }
        }
    }

    /**
     * 通过指定的路径进行 停止监控
     */
    public boolean stopMonitoringByName(String filePath) {
        if (!isMonitoring.get()) {
            return false;
        }
        // 将需要停止监控的路径加入队列
        stopMonitoringQueue.offer(filePath);
        FIFO_CACHE.put(CLOSE + filePath, "close");
        LogUtil.info("putKey:" + CLOSE + filePath);
        MonitorDTO folder = fileMonitorConfig.getMonitoredFolders().stream().filter(k -> k.getFilePath().equals(filePath)).findFirst().orElse(null);
        if (folder != null) {
            folder.setIsRun(false);
        } else {
            return false;
        }
        return true;
    }

    /**
     * 处理停止监控的请求
     */
    private void processStopMonitoringRequests() {
        String filePath;
        while ((filePath = stopMonitoringQueue.poll()) != null) {
            WatchService watchService = watchServices.remove(filePath);
            if (watchService != null) {
                try {
                    watchService.close();
                    LogUtil.info("成功停止对路径 '" + filePath + "' 的监控");
                } catch (Exception e) {
                    LogUtil.info("停止监控失败: " + filePath + ", 错误信息: " + e.getMessage());
                }
            } else {
                LogUtil.info("未找到路径 '" + filePath + "' 的监控服务");
            }
        }
    }

    /**
     * 处理开始监控的请求
     */
    private void processStartMonitoringRequests() {
        MonitorDTO folder;
        while ((folder = startMonitoringQueue.poll()) != null) {
            try {
                /**
                 * 同一个线程中每个文件都有自己的watchService 方便管理
                 */
                WatchService watchService = FileSystems.getDefault().newWatchService();
                /**
                 * 对文件以及子文件夹进行监控注册
                 */
                registerAllDirectories(folder, watchService);
                /**
                 * 将watchService 放置到watchServices中进行统一管理
                 */
                watchServices.put(folder.getFilePath(), watchService);
                LogUtil.info("成功启动对路径 '" + folder.getFilePath() + "' 的监控");
            } catch (Exception e) {
                // TODO 2025 08 11 13:43   保存报错信息
                LogUtil.info("启动监控失败: " + folder.getFilePath() + ", 错误信息: " + e.getMessage());
            }
        }
    }

    /**
     * 处理事件
     */
    private void handleFileChange(WatchEvent<?> event, String fileParentPath) {
        /**
         事件类型
         */
        WatchEvent.Kind<?> kind = event.kind();

        /**
         获取路径
         */
        Path filePath = (Path) event.context();

        /**
         整理成为 绝对地址
         */
        Path fullPath = Paths.get(fileParentPath).resolve(filePath);
        /**
         进行转换
         */
        fileConversionService.convertFile(fileParentPath, fullPath, kind);
    }
}